using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace Spike_EBC_Designer.ViewModel
{
    [Serializable]
    public class Grid : ModelElement
    {
        public Inserter[] ColInserters { get; private set; }
        public Inserter[] RowInserters { get; private set; }

        private Cell[,] _cells;
        private List<Relation> _relations;


        public string Label = "";
        public string Implements = "";
        public string Usings = "";


        public Grid(int colCount, int rowCount)
        {
            InitializeCells(colCount, rowCount);
            InitializeInserters(colCount, rowCount);

            _relations = new List<Relation>();
        }


        private void InitializeCells(int colCount, int rowCount)
        {
            _cells = new Cell[colCount,rowCount];

            for (var col = 0; col < colCount; col++)
                for (var row = 0; row < rowCount; row++)
                    _cells[col, row] = new Cell();
        }

        private void InitializeInserters(int colCount, int rowCount)
        {
            ColInserters = new Inserter[colCount];
            RowInserters = new Inserter[rowCount];

            for (var col = 0; col < colCount; col++)
                ColInserters[col] = new Inserter { Before = col, Command = x => InsertCol(x.Before) };
            for (var row = 0; row < rowCount; row++)
                RowInserters[row] = new Inserter { Before = row, Command = x => InsertRow(x.Before) };
        }


        public void Resize(int width, int height)
        {
            const int INSERTER_SIZE = 10;

            var cellWidth = (width - INSERTER_SIZE) / ColCount;
            var cellHeight = (height - INSERTER_SIZE) / RowCount;

            for (var col = 0; col < this.ColCount; col++)
                for (var row = 0; row < this.RowCount; row++)
                {
                    _cells[col, row].X = INSERTER_SIZE + col * cellWidth;
                    _cells[col, row].Y = INSERTER_SIZE + row * cellHeight;
                    _cells[col, row].Width = cellWidth;
                    _cells[col, row].Height = cellHeight;
                }

            for (var col = 0; col < this.ColCount; col++)
            {
                ColInserters[col].X = INSERTER_SIZE + col*cellWidth;
                ColInserters[col].Y = 0;
                ColInserters[col].Width = cellWidth;
                ColInserters[col].Height = INSERTER_SIZE;
            }

            for (var row = 0; row < this.RowCount; row++)
            {
                RowInserters[row].X = 0;
                RowInserters[row].Y = INSERTER_SIZE + row * cellHeight;
                RowInserters[row].Width = INSERTER_SIZE;
                RowInserters[row].Height = cellHeight;
            }


            foreach (var rel in _relations)
                rel.Resize();
        }


        public int Width
        {
            get { return _cells[this.ColCount - 1, 0].X + _cells[this.ColCount - 1, 0].Width; }
        }

        public int Height
        {
            get { return _cells[0, this.RowCount - 1].Y + _cells[0, this.RowCount - 1].Height; }
        }


        public int ColCount
        {
            get { return _cells.GetLength(0); }
        }

        public int RowCount
        {
            get { return _cells.GetLength(1); }
        }


        public Cell this[int col, int row]
        {
            get { return _cells[col, row]; }
        }


        public Relation[] Relations
        {
            get { return _relations.ToArray(); }
        }


        public object Find(int x, int y)
        {
            for (var col = 0; col < this.ColCount; col++)
                for (var row = 0; row < this.RowCount; row++)
                    if (_cells[col, row].IntersectsWith(x, y))
                        return _cells[col, row];

            for (var col = 0; col < this.ColCount; col++)
                if (ColInserters[col].IntersectsWith(x,y))
                    return ColInserters[col];

            for (var row = 0; row < this.RowCount; row++)
                if (RowInserters[row].IntersectsWith(x, y))
                    return RowInserters[row];

            return null;
        }


        public void AddRelation(Cell from, Cell to)
        {
            _relations.Add(new Relation(from, to));
        }


        public void Select(ModelElement elem)
        {
            Cell cell = elem as Cell;
            for (var col = 0; col < this.ColCount; col++)
                for (var row = 0; row < this.RowCount; row++)
                    _cells[col, row].IsSelected = _cells[col, row] == cell;

            Relation rel = elem as Relation;
            foreach (var r in _relations)
                r.IsSelected = r == rel;
        }


        public ModelElement Selected
        {
            get
            {
                foreach (var cell in _cells)
                    if (cell.IsSelected) return cell;

                foreach (var rel in _relations)
                    if (rel.IsSelected) return rel;

                return this;
            }
        }


        public void InsertRow(int beforeRow)
        {
            var width = this.Width;
            var height = this.Height;

            InitializeInserters(this.ColCount, this.RowCount+1);

            var newCells = new Cell[this.ColCount,this.RowCount + 1];
            for(var col=0;col<this.ColCount;col++)
                for (var row = 0; row <= this.RowCount; row++)
                {
                    if (row < beforeRow)
                        newCells[col, row] = _cells[col, row];
                    else if (row == beforeRow)
                        newCells[col, row] = new Cell();
                    else
                        newCells[col, row] = _cells[col, row-1];
                }
            _cells = newCells;

            this.Resize(width, height);
        }


        public void InsertCol(int beforeCol)
        {
            var width = this.Width;
            var height = this.Height;

            InitializeInserters(this.ColCount+1, this.RowCount);

            var newCells = new Cell[this.ColCount + 1, this.RowCount];
            for (var col = 0; col <= this.ColCount; col++)
                for (var row = 0; row < this.RowCount; row++)
                {
                    if (col < beforeCol)
                        newCells[col, row] = _cells[col, row];
                    else if (col == beforeCol)
                        newCells[col, row] = new Cell();
                    else
                        newCells[col, row] = _cells[col-1, row];
                }
            _cells = newCells;

            this.Resize(width, height);
        }
    }
}